<template>
	<div>
		<div class="header">
			<div class="header-left">
				<span class="label">总数:<span>10</span></span>
				<el-button type="primary" size="mini" @click="pagePlay(pageNum - 1)">上一页</el-button>
				<el-button type="primary" size="mini" @click="pagePlay(pageNum + 1)">下一页</el-button>
				<!--<span class="label">设备编码: <span>{{ playDevice != null ? playDevice.id : '' }}</span></span>-->
				<span class="label">设备编码: <span>{{ toPlayDevice != null ? toPlayDevice.id : '' }}</span></span>
				
			</div>
			<div class="header-right">
				<div>
					<span>分屏</span>
					<span class="spilt" @click="spiltScreen(1)">1</span>
					<span class="spilt" @click="spiltScreen(4)">4</span>
					<span class="spilt" @click="spiltScreen(9)">9</span>
					<span class="spilt" @click="spiltScreen(16)">16</span>
					<span class="spilt" @click="fullScreen">全屏</span>
				</div>
				<div>
					<i class="el-icon-close close" @click="closeAll"></i>
				</div>

			</div>
		</div>
		<div class="play-rect" ref="playRect">
			<!-- v-show="item.isShow" 不能使用v-if指令,会导致缩放后画面消失(v-if条件不成立时是将dom删除) -->
			<div :class="[rectSpilt, item.isSelected ? 'rect-selected' : '', item.amplify ? 'rect-amplify' : '']"
				v-show="item.isShow" 
				v-for="(item, index) in rects" 
				@click="getDeviceInfo(item)"
				@dblclick="adjust(index, item)"
				>
				<i v-if="item.deviceNo != null"
				   @click="closeRect(item)" 
				   class="el-icon-close close"
				   style="float:right;">
				</i>
				<video class="video-element" :ref="'videoElement' + index" >
					
				</video>
			</div>
		</div>
	</div>
</template>
<style scoped>
.close {
	font-size: 22px;
	color: red;

	&:hover {
		cursor: pointer;
	}
	/*position: absolute;*/  
	position: relative;  
	z-index: 2; /* 确保在 video 元素之上 */  
}


.header {
	border: 1px solid rgb(64, 158, 255);
	height: 40px;
	display: flex;
	align-items: center;
	/* 使子项在主轴上均匀分布，第一个在左侧，最后一个在右侧 */
	justify-content: space-between;


	.header-left {

		padding-right: 10px;

		.label {
			margin-left: 10px;
			margin-right: 10px;
			font-size: 12px;

			span {
				color: red;
			}
		}

		.select {
			margin-left: 10px;
		}


	}

	.header-right {
		padding-right: 30px;

		div {
			float: left;
		}

		span {
			padding-right: 10px;
		}

		.spilt {
			color: #1890ff;

			&:hover {
				cursor: pointer;
			}

		}
	}
}

.play-rect {
	margin-top: 5px;
	/*border: 1px solid rgb(64, 158, 255);*/
	display: flex;
	flex-wrap: wrap;
	/* 使得边框和填充包含在宽度和高度内 */
	box-sizing: border-box;

	.rect {
		border: 1px solid rgb(64, 158, 255);
		box-sizing: border-box; /* 使得边框和填充包含在宽度和高度内 */
		position: relative; /* 可以设置为 relative，以便通过 z-index 控制堆叠顺序 */
		/*transition: width 0.3s, height 0.3s;*/ /* 添加放大过渡效果 */
		
		/*播放窗口中的关闭按钮默认隐藏,悬浮时展现*/
		.close{
			display:none;
		}
		&:hover {
			.close{
				display:block;
			}
			.video-element{
				margin-top: -22px;
			}
		}

	}

	.rect-selected {
		border: 3px solid rgb(64, 158, 255);
	}

	.rect-amplify {
		/* 占满整行 */
		/*flex: 1 1 100% !important;*/
		/* 占满整个父div的高度 */
		/*height: 100% !important;*/

		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/1 - 0px) !important;
		/* 边框样式，宽度为0px*/
		height: calc(100%/1 - 0px) !important;


	}
	.rect-split-1 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/1 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/1 - 0px);
		
	}

	.rect-split-2 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/2 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/2 - 0px);
	}

	.rect-split-3 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/3 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/3 - 0px);
	}

	.rect-split-4 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/4 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/4 - 0px);
	}
	.video-element{
		width:100%;
		object-fit: fill; /*填满整个div,两侧不留空白*/
		height:100%;
		position: relative; /* 可以设置为 relative，以便通过 z-index 控制堆叠顺序 */  
    	z-index: 1; /* 确保 video 在其他内容之下，但在背景之上 */ 
	}
}
</style>

<script>
import { mapState } from 'vuex';
import flvjs from 'flv.js'; 
import {playVideo} from '@/api/vplat/device'; 

export default {
	name: "PlayWindow",
	dicts: [],
	data() {
		return {
			pageNum: 1,
			//初始化播放窗口数量
			rectSize: 4,
			rects: [],
			//rects: Array.from({ length: 9 }, (_, i) => ({ id: i, isSelected: false })),
			//初始化播放窗口的分屏样式
			rectSpilt: 'rect rect-split-2',

		};
	},
	mounted() {
		this.setFullHeight();
		this.setRects(this.rectSize);
	},
	computed: {//每次渲染都会触发
		...mapState({
			//引入模块化变量
			toPlayDevice: state => state.video.playDevice,

		}),
		//双击设备树后获取播放的设备参数
		/*
		playDevice() {
			if (this.$store.state.video.playDevice) {
				let device = this.$store.state.video.playDevice;
				const exist = this.rects.find(rect => rect.deviceNo === device.id);
				if (!exist) {
					for (let i = 0; i < this.rects.length; i++) {
						var o = this.rects[i];
						if (o.deviceNo == null) {
							o.deviceNo = this.$store.state.video.playDevice.id;
							o.name = this.$store.state.video.playDevice.name;
							break;
						}
					}
				}
				return device;
			}
			return null;
		}
		*/
	},
	watch: {
		//监听模块化变量变化 
		toPlayDevice(device) {
			//console.log(device);
			if (device) {
				const exist = this.rects.find(rect => rect.deviceNo === device.id);
				if (!exist) {
					for (let i = 0; i < this.rects.length; i++) {
						let item = this.rects[i];
						if (item.deviceNo == null) {
							item.deviceNo = device.id;
							item.name = device.name;
							this.active(item);
							item.videoElement = this.$refs['videoElement' +  i][0];
							this.getPlayUrls(item);
							break;
						}
					}
				}
			}
		},
	},
	beforeDestroy() {
		this.closeAll();
	},
	methods: {
		//
		closeAll() {
			this.rects.forEach((o, i) => {
				this.closeRect(o);
			});
		},
		//创建播放器,加载视频
		createPlayer(item) {
			/** 播放器类型,区分h265和h265播放器 [Flvjs | JPlayer | NodePlayer]*/
			let playerType = "Flvjs";
			let playUrl = item.playUrls.websocketUrl;
			// 若url包含265,此时 采用265播放器播放
			if (playUrl.includes("H265")) {
				//playerType = "JPlayer";
				playerType = "NodePlayer";
			}
			item.playerType = playerType;
			if(playerType == "Flvjs"){
				this.createFlvjsPlayer(item);
			}else if(playerType == "JPlayer"){
				
			}else if(playerType == "NodePlayer"){
				
			}
		},
		createFlvjsPlayer(item) {
			if (flvjs.isSupported()) {
				let player = flvjs.createPlayer({
			        type: 'flv',					//媒体类型
			        url: item.playUrls.websocketUrl,//flv格式媒体URL
			        isLive: true,					//数据源是否为直播流
			        //hasAudio: false,				//数据源是否包含有音频
				    hasVideo: true,					//数据源是否包含有视频
				    withCredentials: false, // 是否需要证书认证
		            cors: true, // 开启跨域访问
			    },{
		              enableWorker: false, // 开启flv.js的Worker，多进程运行flv.js提升解析速度可以优化延迟。 default false
		              enableStashBuffer: false, // Enable IO stash buffer. Set to false if you need realtime (minimal latency) for live stream playback, but may stalled if there's network jittering. default true
		              stashInitialSize: 384, // Indicates IO stash buffer initial size. Default is 384KB. Indicate a suitable size can improve video load/seek time.
		              seekType: "range", // 'range' use range request to seek, or 'param' add params into url to indicate request range.
		              lazyLoad: false, // Abort the http connection if there's enough data for playback. default true
		              lazyLoadMaxDuration: 10, // Indicates how many seconds of data to be kept for 'lazyLoad'.
		              lazyLoadRecoverDuration: 10, // Indicates the 'lazyLoad' recover time boundary in seconds.
		              deferLoadAfterSourceOpen: false, // Do load after MediaSource sourceopen event triggered. On Chrome, tabs which be opened in background may not trigger sourceopen event until switched to that tab. default true
		              //statisticsInfoReportInterval: 600,
		              fixAudioTimestampGap: false, //主要是这个配置，直播时音频的码率会被降低，直播游戏时背景音会有回响，但是说话声音没问题 Fill silent audio frames to avoid a/v unsync when detect large audio timestamp gap. default true
		              // 下面这个控制很有用，主要是用来控制video播放器缓冲区this.buffered中的保留数据的；
		              // 当this.buffered.end(this.buffered.length - 1) - this.buffered.start(0) >= autoCleanupMaxBackwardDuration秒时，
		              // 立即对缓冲区中的数据进行清理，确保只保留最后的autoCleanupMinBackwardDuration秒数据
		              // 所以这个设置能够减少浏览器占用的内存空间，对于同时播放n路视频来说简直是太有意义了
		              // 因为在progress事件里面我的追帧策略是最高16倍速时追帧到16秒内，所以这里autoCleanupMinBackwardDuration的值要大于16
		              autoCleanupSourceBuffer: true, // Do auto cleanup for SourceBuffer. default false.
		              autoCleanupMaxBackwardDuration: 10, // When backward buffer duration exceeded this value (in seconds), do auto cleanup for SourceBuffer. default 3 * 60
		              autoCleanupMinBackwardDuration: 10, // Indicates the duration in seconds to reserve for backward buffer when doing auto cleanup. default 2 * 60
		              reuseRedirectedURL: true,
		            }
			    );
			    
				player.attachMediaElement(item.videoElement);//将播放实例注册到节点
				player.load(); 					//加载数据流
				player.play();					//播放数据流
				item.player = player;//播放器与播放方格的数据对象绑定
				
				
				item.startPlayTime = new Date();	//开始时间
				if(item.intervalId == null){
					//注册定时器观察播放是否正常
				    let intervalId = setInterval(() => {
				    	/*
					    console.log(videoElement.currentTime);//设置或者返回视频中的当前播放位置（以秒为单位）
					    console.log(videoElement.duration);//返回视频的长度（以秒为单位）
					    console.log(videoElement.ended);//返回视频的播放是否以及结束
					    console.log(videoElement.error);//返回视频错误状态的MediaError对象
					    console.log(videoElement.currentSrc);
					    console.log(videoElement.paused);
					    console.log(videoElement.muted);
					    console.log(videoElement.muted);
					    console.log(videoElement.volume);
					    console.log(videoElement.width);
					    console.log(videoElement.height);
					    */
				    
					    let sec = (new Date().getTime() - item.startPlayTime.getTime())/1000;
					    if(sec - item.videoElement.currentTime > 15){
					    	console.log("播放异常");
					    	this.replayVideo(item);
					    }
					}, 10000);
					//使用定时作异常监听即可
					/*
			        player.on("error", (e) => {//不能监听流媒体重启情况
			        	console.log("资源播放异常", e);
			        	_this.replayVideo("资源播放异常",playerNo,deviceNo);
			         });
					*/
					item.intervalId = intervalId;//播放器与播放方格的数据对象绑定
				}
			    
			    
				
				
			}else{
				console.warn('flv.js is not supported in your browser');
			}

		},
		//发送请求获取播放url
		getPlayUrls(item) {
			try{
				let result = {
				    "param": {
				        "httpFlvUrl": "http://47.107.226.11:8024/live/20230925044756.flv",
				        "rtmpUrl": " rtmp://47.107.226.11:1935/live/20230925044756",
				        "websocketUrl": "ws://47.107.226.11:8024/live/20230925044756.flv",
				        "iMessageCode": 0,
				        "wsFlvUrl": " ws://47.107.226.11:8024/live/20230925044756.flv"
				    }
				};
				//let result = playVideo({deviceNo:item.deviceNo});
				item.playUrls = result.param;
				this.createPlayer(item);
			} catch (e) {
				console.log("播放异常,15秒后得试", e);
				if(item.intervalId == null){ //item.intervalId == null为首次请求就失败
					this.replayVideo(item);
				}
			}
			

		},
		//重新播放
		replayVideo(item) {
			this.closePlayer(item);
			//this.closeIntervalId(item);
			setTimeout(() => {
    			this.getPlayUrls(item);
			}, 5000);
		},
		//创建新的播放格子
		createRect(index) {
			let item = {
				id: index,//在播放窗口中的索引
				deviceNo: null,//设备编码
				isSelected: false,//是否被选中
				amplify: false,//是否放大
				isShow: true,//是否展示
				videoElement: null,//播放器绑定的dom
				player: null,//播放器
				playUrls: null,//播放的url
				playerType: null,//播放器的类型
				intervalId: null,//监听播放器播放情况的定时任务
			};
			return item;

		},
		//双击播放格式缩放
		adjust(index, item) {
			/*
			// 切换当前div的选中状态  
			this.rects[index].amplify = !this.rects[index].amplify;
			// 如果当前div被选中，则取消其他所有div的选中状态  
			if (this.rects[index].amplify) {
				this.rects = this.rects.map((rect, i) => ({
					...rect,
					amplify: i === index,
				}));
			}
			*/

			this.rects.forEach(function (o, i) {
				if (o.id != item.id) {
					o.isShow = !o.isShow;
				}
			});
			item.amplify = !item.amplify;
			item.isShow = true;


		},
		//单击播放格式获取播放信息
		getDeviceInfo(item) {
			this.active(item);
		},
		//设置宫格选中状态
		active(item) {
			//this.rects.map(o => o.isSelected = false);
			this.rects.forEach(function (o, i) {
				o.isSelected = false
			});
			item.isSelected = true;
		},
		//销毁播放器
		closePlayer(item) {
			console.log("closePlayer");
			let playerType = item.playerType;
			if(playerType == "Flvjs"){
				// 组件销毁前，确保销毁 FlvPlayer 实例  
			    if (item.player != null) {
				  item.videoElement.src = ""; 
			      item.player.pause();  
			      item.player.unload();  
			      item.player.detachMediaElement();  
			      item.player.destroy();  
			      item.player = null;  
			      //console.log("销毁播放器 ",playerType);
			    }
			}else if(playerType == "JPlayer"){
				//h265播放器
				player.stop(true);
				player = null;
			}else if(playerType == "NodePlayer"){
				//h265播放器
				player.stop();
			    player.clearView();
			}
		},
		//销毁播放监听的定时器
		closeIntervalId(item){
			console.log("closeIntervalId");
			let playerType = item.playerType;
			if(playerType == "Flvjs"){
			    if(item.intervalId != null){
					clearInterval(item.intervalId);
				}
			}else if(playerType == "JPlayer"){
				
			}else if(playerType == "NodePlayer"){
				
			}
		},
		//关闭播放格子,销毁播放器
		closeRect(item) {
			item.deviceNo = null;
			this.closePlayer(item);
			this.closeIntervalId(item);
			
		},
		//分页播放
		pagePlay(list) {
			console.log(list);
		},
		//切换分屏数量
		spiltScreen(newSize) {
			//切换前先要恢复非放大的样式,否则切换后样式计算错误
			this.rects.forEach(function (o, i) {
				o.isShow = true;
				o.amplify = false;
			});
			//求一个数的平方根
			let squareRoot = Math.sqrt(newSize);
			this.rectSpilt = 'rect rect-split-' + squareRoot;
			this.setRects(newSize);

		},
		//全屏
		fullScreen() {
			const elem = this.$refs.playRect;
			if (!document.fullscreenElement &&
				!document.mozFullScreenElement && !document.msFullscreenElement &&
				!document.webkitFullscreenElement && !document.webkitCurrentFullScreenElement) {
				// 当前浏览器未处于全屏状态  
				if (elem.requestFullscreen) {
					elem.requestFullscreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err.message);
					});
				} else if (elem.mozRequestFullScreen) { /* Firefox */
					elem.mozRequestFullScreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err);
					});
				} else if (elem.webkitRequestFullscreen) { /* Chrome, Safari 和 Opera */
					elem.webkitRequestFullscreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err);
					});
				} else if (elem.msRequestFullscreen) { /* IE/Edge */
					elem.msRequestFullscreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err);
					});
				}
			} else {
				// 当前浏览器已处于全屏状态  
				if (document.exitFullscreen) {
					document.exitFullscreen();
				} else if (document.mozCancelFullScreen) { /* Firefox */
					document.mozCancelFullScreen();
				} else if (document.webkitExitFullscreen) { /* Chrome, Safari 和 Opera */
					document.webkitExitFullscreen();
				} else if (document.msExitFullscreen) { /* IE/Edge */
					document.msExitFullscreen();
				}
			}
		},
		//设备播放格子数量
		setRects(newSize) {
			if (newSize > this.rectSize) {//增加宫格
				for (let i = this.rectSize; i < newSize; i++) {
					this.rects.push(this.createRect(i));

				}
			} else if (newSize < this.rectSize) {//减少宫格
				let tmpArr = [];
				for (let i = 0; i < this.rectSize; i++) {
					let item = this.rects[i];
					if(i < newSize){
						tmpArr.push(item);
					}else{
						this.closeRect(item);
					}
					
				}
				//this.$store.dispatch('video/addPlayAction', null);
				//this.$store.commit('video/addPlay', data);
				this.rects = tmpArr;
			} else {
				if (this.rects.length == 0) {//初始化
					for (let i = 0; i < this.rectSize; i++) {
						this.rects.push(this.createRect(i));
					}
				}
			}
			this.rectSize = newSize;
			//console.log(this.rects);


		},
		//设备播放窗口占满整个div剩余高度
		setFullHeight() {
			let playRect = this.$refs.playRect;
			//方式一:
			var offsetTop = this.getElementOffsetTop(playRect);
			//方式二:
			//var offsetTop = playWindow.getBoundingClientRect().top;
			//浏览器窗口高度
			var browserHeight = window.innerHeight;
			//使有js动态设置高度,20及5为外层div、自身的padding/margin引起的偏移(可通过观察盒子模型得到)
			playRect.style.height = (browserHeight - offsetTop - 20 - 5) + "px";
		},
		//获取html元素距顶部的固定距离
		getElementOffsetTop(element) {
			var top = 0;
			while (element) {
				top += element.offsetTop || 0;
				element = element.offsetParent;
			}
			return top;
		},
	}
};
</script>

